using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.Networking;
using UObject = UnityEngine.Object;
public struct Version
{
    public int bigVersion;
    public int smallVersion;
    public int update;
    public Version(int i1, int i2, int i3)
    {
        this.bigVersion = i1;
        this.smallVersion = i2;
        this.update = i3;
    }
}
public class HotUpdate : MonoBehaviour
{
    byte[] m_ReadPathFileListData;

    byte[] m_ServerFileListData;

    private Version new_Version;
    private Version curr_Version;
    internal class DownFileInfo
    {
        public string url;
        public string fileName;
        public string MD5;
        public DownloadHandler fileData;
    }
    int m_DownloadCount;


    /// <summary>
    /// 下载单个文件
    /// </summary>
    /// <param name="url"></param>
    /// <returns></returns>
    IEnumerator DownloadFile(DownFileInfo info, Action<DownFileInfo> Complete)
    {
        UnityWebRequest webRequest = UnityWebRequest.Get(info.url);
        yield return webRequest.SendWebRequest();
        if (webRequest.result == UnityWebRequest.Result.ProtocolError || webRequest.result == UnityWebRequest.Result.ConnectionError)
        {
            Debug.LogError("下载文件出错：" + info.url);
            yield break;
        }
        Debug.Log("下载文件" + info.url);
        yield return new WaitForSeconds(0.3f);
        info.fileData = webRequest.downloadHandler;
        Complete?.Invoke(info);
        webRequest.Dispose();//释放
    }
    /// <summary>
    /// 下载多个文件
    /// </summary>
    /// <param name="info"></param>
    /// <param name="Complete"></param>
    /// <returns></returns>
    IEnumerator DownloadFiles(List<DownFileInfo> infos, Action<DownFileInfo> Complete, Action DownloadAllComplete)
    {
        foreach (var info in infos)
        {
            yield return DownloadFile(info, Complete);
        }
        DownloadAllComplete?.Invoke();
    }
    /// <summary>
    /// 获取filelist
    /// </summary>
    /// <param name="fileData"></param>
    /// <returns></returns>
    private List<DownFileInfo> GetFileList(string fileData, string path/*需要操作的目标文件夹路径*/)
    {
        string content = fileData.Trim().Replace("\r", "");//规范化
        string[] files = content.Split("\n").Where(s => !string.IsNullOrEmpty(s)).ToArray();//切割每一行

        string[] versions = files[files.Length - 1].Split(".");
        new_Version = new Version(int.Parse(versions[0]), int.Parse(versions[1]), int.Parse(versions[2]));

        //去除版本号
        string[] newFiles = new string[files.Length - 1];
        Array.Copy(files, newFiles, newFiles.Length);
        files = newFiles;

        List<DownFileInfo> downFileInfos = new List<DownFileInfo>(files.Length);
        //每一行
        foreach (var file in files)
        {
            string[] allInfo = file.Split("$");
            string[] temp_info = allInfo[0].Split("|");
            string[] info = new string[3];
            info[2] = allInfo[1];//MD5
            info[0] = temp_info[0];
            info[1] = temp_info[1];
            DownFileInfo fileInfo = new DownFileInfo();
            fileInfo.fileName = info[1];//.../.../...ab
            fileInfo.url = Path.Combine(path, info[1]);
            fileInfo.MD5 = allInfo[1];
            downFileInfos.Add(fileInfo);
        }
        return downFileInfos;
    }
    GameObject loadingObj;
    LoadingUI loadingUI;


    private void Start()
    {
        GameObject go = Resources.Load<GameObject>("LoadingUI");
        loadingObj = Instantiate(go);
        loadingObj.transform.SetParent(this.transform);
        loadingUI = loadingObj.GetComponent<LoadingUI>();
        if (IsFirstInstall())
        {
            ReleaseResources();
        }
        else
        {
            CheckUpdate();
        }
    }
    private bool IsFirstInstall()
    {
        //判断只读目录是否有版本文件
        bool isExistReadPath = FileUtil.IsExist(Path.Combine(PathUtil.ReadPath, AppConst.FileListName));
        //判断可读写目录
        bool isExistReadWritePath = FileUtil.IsExist(Path.Combine(PathUtil.ReadWritePath, AppConst.FileListName));
        return isExistReadPath && !isExistReadWritePath;
    }
    private void ReleaseResources()
    {
        m_DownloadCount = 0;
        string url = Path.Combine(PathUtil.ReadPath, AppConst.FileListName);
        DownFileInfo info = new DownFileInfo();
        info.url = url;
        StartCoroutine(DownloadFile(info, OnDownloadReadPathFileListComplete));
    }

    private void OnDownloadReadPathFileListComplete(DownFileInfo file)
    {
        m_ReadPathFileListData = file.fileData.data;
        List<DownFileInfo> fileInfos = GetFileList(file.fileData.text, PathUtil.ReadPath);
        StartCoroutine(DownloadFiles(fileInfos, OnReleaseFileComplete, OnReleaseFileAllComplete));
        loadingUI.InitProgress(fileInfos.Count, "正在释放资源，不消耗流量...");
    }

    private void OnReleaseFileAllComplete()
    {//把filelsit写到可读写目录
        FileUtil.WriteFile(Path.Combine(PathUtil.ReadWritePath, AppConst.FileListName), m_ReadPathFileListData);
        CheckUpdate();
    }

    private void OnReleaseFileComplete(DownFileInfo info)
    {
        Debug.Log("OnReleaseFileComplete:" + info.url);
        string writeFile = Path.Combine(PathUtil.ReadWritePath, info.fileName);
        FileUtil.WriteFile(writeFile, info.fileData.data);
        m_DownloadCount++;
        loadingUI.UpdateProgress(m_DownloadCount);
    }
    public static string GetCurrFileList(string persistentDataPath)
    {

        string targetFilePath = Path.Combine(persistentDataPath, "filelist.txt");

        // 检查文件是否存在
        if (File.Exists(targetFilePath))
        {
            // 读取文件内容
            string fileList = File.ReadAllText(targetFilePath);
            return fileList;
        }
        else
        {
            Debug.Log("File does not exist: " + targetFilePath);

            return null;
        }


    }
    private void CheckUpdate()
    {
        string url = Path.Combine(AppConst.ResourcesUrl, AppConst.FileListName);
        DownFileInfo info = new DownFileInfo();
        info.url = url;
        StartCoroutine(DownloadFile(info, OnDownloadServerFileListComplete));//这里是下载filelist
    }
    Dictionary<string/*fileName*/, string/*MD5*/> FileList2Dict(string curr_FileInfos)
    {
        Dictionary<string, string> currFiles = new Dictionary<string, string>();
        string content = curr_FileInfos.Trim().Replace("\r", "");//规范化
        string[] files = content.Split("\n");//切割每一行

        string[] versions = files[files.Length - 1].Split(".");
        curr_Version = new Version(int.Parse(versions[0]), int.Parse(versions[1]), int.Parse(versions[2]));
        //去除版本号
        string[] newFiles = new string[files.Length - 1];
        // 复制原数组中的元素到新数组，除了最后一个
        Array.Copy(files, newFiles, newFiles.Length);
        files = newFiles;

        //每一行
        foreach (var file in files)
        {
            string[] allInfo = file.Split("$");
            string[] info = allInfo[0].Split("|");
            currFiles[info[1]] = allInfo[1];//MD5
        }
        return currFiles;
    }

    private void OnDownloadServerFileListComplete(DownFileInfo file)//下载完filelist后，比对本地的资源，检测出实际需要下载的资源
    {



        m_DownloadCount = 0;
        m_ServerFileListData = file.fileData.data;
        List<DownFileInfo> fileInfos = GetFileList(file.fileData.text, AppConst.ResourcesUrl);//新版文件

        //解析成键值对 初始表示需要删除的文件列表，如果移除就表示不需要删除
        Dictionary<string, string> currFiles = FileList2Dict(GetCurrFileList(PathUtil.ReadWritePath));//当前版本文件

        //TODO对比版本号
        if (object.Equals(curr_Version, new_Version))
        {
            EnterGame();
            return;
        }
        //需要下载的文件集合
        List<DownFileInfo> downFileInfos = new List<DownFileInfo>();
        if (currFiles.Count == 0)
        {
            foreach (DownFileInfo info in fileInfos)
            {
                downFileInfos.Add(info);
            }
        }
        else
        {

            //对比currFiles和fileInfos
            foreach (DownFileInfo info in fileInfos)
            {
                string localFile = Path.Combine(PathUtil.ReadWritePath, info.fileName);
                if (!FileUtil.IsExist(localFile))
                {
                    info.url = Path.Combine(AppConst.ResourcesUrl, info.fileName);
                    downFileInfos.Add(info);
                }
                else
                {//存在对比本地MD5
                    string curr_md5;
                    if (currFiles.TryGetValue(info.fileName, out curr_md5) && curr_md5 == info.MD5)
                    {
                        currFiles.Remove(info.fileName);//不需要删除
                    }
                    else
                    {
                        info.url = Path.Combine(AppConst.ResourcesUrl, info.fileName);
                        downFileInfos.Add(info);

                    }
                }
            }
            //删除
            DeleteFiles(currFiles, PathUtil.ReadWritePath);
        }
        if (downFileInfos.Count > 0)//需要更新
        {
            StartCoroutine(DownloadFiles(downFileInfos, OnUpdateFileComplete, OnUpdateFileAllComplete));
            loadingUI.InitProgress(downFileInfos.Count, " 正在更新...");
        }
        else
        {
            EnterGame();
        }
    }

    private void DeleteFiles(Dictionary<string/*fileName*/, string/*md5不需要使用*/> currFiles, string DeletePath)
    {
        foreach (var file in currFiles.Keys)
        {
            string fullPath = Path.Combine(DeletePath, file);
            if (File.Exists(fullPath))
            {
                try
                {
                    File.Delete(fullPath);
                    Debug.Log($"Deleted file: {fullPath}");
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Error deleting file {fullPath}: {ex.Message}");
                }
            }
            else
            {
                Debug.LogError($"删除文件失败，检查逻辑: {fullPath}");
            }
        }
    }

    private void EnterGame()
    {
        Manager.Event.Fire((int)GameEvent.GameInit);
        Destroy(loadingObj);//loading界面

    }

    private void OnUpdateFileComplete(DownFileInfo info)
    {
        Debug.Log("OnUpdateFileComplete:" + info.url);
        string writeFile = Path.Combine(PathUtil.ReadWritePath, info.fileName);
        FileUtil.WriteFile(writeFile, info.fileData.data);
        m_DownloadCount++;
        loadingUI.UpdateProgress(m_DownloadCount);
    }

    private void OnUpdateFileAllComplete()
    {
        FileUtil.WriteFile(Path.Combine(PathUtil.ReadWritePath, AppConst.FileListName), m_ServerFileListData);
        EnterGame();
        loadingUI.InitProgress(0, "正在载入...");
    }

    private void OnComplete(UObject obj)
    {
        GameObject go = Instantiate(obj) as GameObject;
        go.transform.SetParent(transform);
        go.SetActive(true);
        go.transform.localPosition = Vector3.zero;
    }

}
